package com.bkck.sharkblockade.levels.block;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.bkck.sharkblockade.levels.ball.Ball;
import com.bkck.sharkblockade.levels.block.Block;
import com.bkck.sharkblockade.levels.lines.Line;

/**
 * A collection of lines for the edges of the blocks. This collection consists of collections of
 * left, right, top, and bottom edges separately.
 * 
 * @author BKCK
 * 
 */
public class BlockEdges {

  private List<Line> leftEdges;
  private List<Line> rightEdges;
  private List<Line> topEdges;
  private List<Line> bottomEdges;

  public BlockEdges() {
    leftEdges = new ArrayList<Line>();
    rightEdges = new ArrayList<Line>();
    topEdges = new ArrayList<Line>();
    bottomEdges = new ArrayList<Line>();
  }

  /**
   * Add this block's edges to the collection.
   * 
   * @param block
   */
  public void addBlockEdges(Block block, HashMap<Line, Block> map) {
    float rightEdgeX = block.getX() + block.getWidth();
    float bottomEdgeY = block.getY() + block.getHeight();

    Line topEdge = new Line(block.getX(), block.getY(), rightEdgeX, block.getY());
    Line bottomEdge = new Line(block.getX(), bottomEdgeY, rightEdgeX, bottomEdgeY);
    Line leftEdge = new Line(block.getX(), block.getY(), block.getX(), bottomEdgeY);
    Line rightEdge = new Line(rightEdgeX, block.getY(), rightEdgeX, bottomEdgeY);

    block.applyEdges(leftEdge, rightEdge, topEdge, bottomEdge);

    map.put(topEdge, block);
    map.put(bottomEdge, block);
    map.put(leftEdge, block);
    map.put(rightEdge, block);

    addToTopEdges(topEdge);
    addToBottomEdges(bottomEdge);
    addToLeftEdges(leftEdge);
    addToRightEdges(rightEdge);
  }

  public void removeBlockEdges(Block block) {
    leftEdges.remove(block.getLeftEdge());
    rightEdges.remove(block.getRightEdge());
    topEdges.remove(block.getTopEdge());
    bottomEdges.remove(block.getBottomEdge());
  }

  /**
   * Add/Insert a left edge to the list of left edges. The list should be ordered from leftmost to
   * rightmost based on their x coordinates.
   * 
   * @param line
   */
  public void addToLeftEdges(Line line) {
    if (leftEdges.size() == 0) {
      leftEdges.add(line);
      return;
    }

    for (int i = 0; i < leftEdges.size(); i++) {
      if (leftEdges.get(i).getPoint1().getX() >= line.getPoint1().getX()) {
        leftEdges.add(i, line);
        return;
      }
    }
    leftEdges.add(line);
  }

  /**
   * Add/Insert a right edge to the list of right edges. The list should be ordered from rightmost
   * to leftmost based on their x coordinates.
   * 
   * @param line
   */
  public void addToRightEdges(Line line) {
    if (rightEdges.size() == 0) {
      rightEdges.add(line);
      return;
    }
    for (int i = 0; i < rightEdges.size(); i++) {
      if (rightEdges.get(i).getPoint1().getX() <= line.getPoint1().getX()) {
        rightEdges.add(i, line);
        return;
      }
    }
    rightEdges.add(line);
  }

  /**
   * Add/Insert a top edge to the list of top edges. The list should be ordered from highest to
   * lowest based on their y coordinates.
   * 
   * @param line
   */
  public void addToTopEdges(Line line) {
    if (topEdges.size() == 0) {
      topEdges.add(line);
      return;
    }
    for (int i = 0; i < topEdges.size(); i++) {
      if (topEdges.get(i).getPoint1().getY() >= line.getPoint1().getY()) {
        topEdges.add(i, line);
        return;
      }
    }
    topEdges.add(line);
  }

  /**
   * Add/Insert a bottom edge to the list of bottom edges. The list should be ordered from lowest to
   * highest based on their y coordinates.
   * 
   * @param line
   */
  public void addToBottomEdges(Line line) {
    if (bottomEdges.size() == 0) {
      bottomEdges.add(line);
      return;
    }
    for (int i = 0; i < bottomEdges.size(); i++) {
      if (bottomEdges.get(i).getPoint1().getY() <= line.getPoint1().getY()) {
        bottomEdges.add(i, line);
        return;
      }
    }
    bottomEdges.add(line);
  }

  public List<Line> getLeftEdges() {
    return leftEdges;
  }

  public List<Line> getRightEdges() {
    return rightEdges;
  }

  public List<Line> getTopEdges() {
    return topEdges;
  }

  public List<Line> getBottomEdges() {
    return bottomEdges;
  }

  /**
   * Whether the ball is to the left of all blocks
   * 
   * @param ball
   * @return
   */
  public boolean ballIsLeftToLeftmostEdge(Ball ball) {
    // special case: no blocks left
    if (leftEdges.size() == 0) {
      return true;
    }
    if (leftEdges.get(0).getPoint1().getX() > ball.getX() + ball.getWidth()
        && leftEdges.get(0).getPoint1().getX() > ball.getPreviousX() + ball.getWidth()) {
      return true;
    }
    return false;
  }

  /**
   * Whether the ball is to the right of all blocks
   * 
   * @param ball
   * @return
   */
  public boolean ballIsRightToRightmostEdge(Ball ball) {
    // special case: no blocks left
    if (rightEdges.size() == 0) {
      return true;
    }
    if (rightEdges.get(0).getPoint1().getX() < ball.getX()
        && rightEdges.get(0).getPoint1().getX() < ball.getPreviousX()) {
      return true;
    }
    return false;
  }

  /**
   * Whether the ball is above all blocks.
   * @param ball
   * @return
   */
  public boolean ballIsAboveHighestEdge(Ball ball) {
    // special case: no blocks left
    if (topEdges.size() == 0) {
      return true;
    }
    if (topEdges.get(0).getPoint1().getY() > ball.getY() + ball.getHeight()
        && topEdges.get(0).getPoint1().getY() > ball.getPreviousY() + ball.getHeight()) {
      return true;
    }
    return false;
  }

  /**
   * Whether the ball is below all blocks.
   * @param ball
   * @return
   */
  public boolean ballIsBelowLowestEdge(Ball ball) {
    // special case: no blocks left
    if (bottomEdges.size() == 0) {
      return true;
    }
    if (bottomEdges.get(0).getPoint1().getY() < ball.getY()
        && bottomEdges.get(0).getPoint1().getY() < ball.getPreviousY()) {
      return true;
    }
    return false;
  }

  public String leftEdgesToString() {
    String edges = "";
    for (Line edge : leftEdges) {
      edges = edges + edge.toString() + "\n";
    }
    return edges;
  }

  public String rightEdgesToString() {
    String edges = "";
    for (Line edge : rightEdges) {
      edges = edges + edge.toString() + "\n";
    }
    return edges;
  }

  public String topEdgesToString() {
    String edges = "";
    for (Line edge : topEdges) {
      edges = edges + edge.toString() + "\n";
    }
    return edges;
  }

  public String bottomEdgesToString() {
    String edges = "";
    for (Line edge : bottomEdges) {
      edges = edges + edge.toString() + "\n";
    }
    return edges;
  }

  public void clear() {
    leftEdges.clear();
    rightEdges.clear();
    topEdges.clear();
    bottomEdges.clear();
  }
}
